package com.huaxin.hxenum.base;

import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedJdbcTypes;

import java.sql.CallableStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;

/**
 * 通用枚举 TypeHandler 基类
 * 注意：这是一个抽象基类，子类必须继承并传入具体枚举类型
 *
 * @param <E> 枚举类型，必须实现 BaseCodeEnum<T>
 * @param <T> code 的类型（String, Integer 等）
 */
@MappedJdbcTypes({JdbcType.VARCHAR, JdbcType.INTEGER, JdbcType.BIGINT, JdbcType.NUMERIC})
public abstract class BaseCodeEnumTypeHandler<E extends Enum<E> & BaseCodeEnum<T>, T>
        extends BaseTypeHandler<E> {

    // 缓存：code -> enum 实例映射
    private static final Map<Class<?>, Map<?, ?>> LOOKUP_CACHE = new HashMap<>();

    protected final Class<E> enumClass;
    protected final Class<T> codeType;

    protected BaseCodeEnumTypeHandler(Class<E> enumClass) {
        if (enumClass == null) {
            throw new IllegalArgumentException("Enum class cannot be null");
        }
        this.enumClass = enumClass;
        this.codeType = resolveCodeType(enumClass);
    }

    @SuppressWarnings("unchecked")
    private Class<T> resolveCodeType(Class<E> enumClass) {
        java.lang.reflect.Type genericInterface = enumClass.getGenericInterfaces()[0];
        if (genericInterface instanceof java.lang.reflect.ParameterizedType pt) {
            return (Class<T>) pt.getActualTypeArguments()[0];
        }
        throw new IllegalArgumentException(
                "Enum " + enumClass.getSimpleName() + " must specify type in BaseCodeEnum<T>");
    }

    @SuppressWarnings("unchecked")
    private Map<T, E> getLookupMap() {
        return (Map<T, E>) LOOKUP_CACHE.computeIfAbsent(enumClass, cls -> {
            Map<T, E> map = new HashMap<>();
            for (E e : enumClass.getEnumConstants()) {
                map.put(e.getCode(), e);
            }
            return map;
        });
    }

    // =============== 读取数据库 ===============

    @Override
    public E getNullableResult(ResultSet rs, String columnName) throws SQLException {
        T code = rs.getObject(columnName, codeType);
        return rs.wasNull() ? null : getLookupMap().get(code);
    }

    @Override
    public E getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        T code = rs.getObject(columnIndex, codeType);
        return rs.wasNull() ? null : getLookupMap().get(code);
    }

    @Override
    public E getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        T code = cs.getObject(columnIndex, codeType);
        return cs.wasNull() ? null : getLookupMap().get(code);
    }

    // =============== 写入数据库 ===============
    // ❗关键：不要重写 setParameter！让子类去处理，避免桥方法冲突
}